from typing import Generator, List
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
from pydantic import ValidationError
from sqlalchemy.orm import Session
from redis import Redis

from ..db.session import SessionLocal
from ..core.config import settings
from ..core.cache import get_redis_connection
from ..schemas.auth import TokenPayload
from ..crud.user import user_crud
from ..models.user import User

oauth2_scheme = OAuth2PasswordBearer(tokenUrl=f"{settings.API_V1_STR}/auth/login")


def get_db() -> Generator:
    """
    获取数据库会话
    """
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()


def get_redis() -> Redis:
    """
    获取Redis连接
    """
    return get_redis_connection()


def get_current_user(
        db: Session = Depends(get_db), token: str = Depends(oauth2_scheme)
) -> User:
    """
    获取当前用户
    """
    try:
        payload = jwt.decode(
            token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
        )
        token_data = TokenPayload(**payload)
    except (JWTError, ValidationError):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="无法验证凭据",
            headers={"WWW-Authenticate": "Bearer"},
        )

    # 尝试从缓存获取用户
    redis = get_redis_connection()
    cached_user = redis.get(f"user:{token_data.sub}")

    if cached_user:
        # 如果缓存存在则直接返回
        import json
        user_dict = json.loads(cached_user)
        user = User(**user_dict)
    else:
        # 否则从数据库获取
        user = user_crud.get(db, id=token_data.sub)
        if not user:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail="用户不存在"
            )

        # 缓存用户数据
        import json
        user_dict = {c.name: getattr(user, c.name) for c in user.__table__.columns}
        redis.setex(
            f"user:{user.id}",
            settings.USER_CACHE_EXPIRE_SECONDS,
            json.dumps(user_dict)
        )

    return user


def get_current_user_with_permissions(allowed_roles: List[str]):
    """
    获取当前已登录用户，并验证用户角色是否在允许的角色列表中

    参数：
    - allowed_roles: 允许访问的角色列表

    返回：
    - 依赖函数，用于获取当前有权限的用户
    """

    def _get_current_user_with_permissions(
            current_user: User = Depends(get_current_user),
    ) -> User:
        if current_user.role not in allowed_roles:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="权限不足，无法访问"
            )
        return current_user

    return _get_current_user_with_permissions
